<template>
    <div class="home">
        <el-upload class="upload-demo" action="" :before-upload="handleFileChange" :on-remove="handleRemove"
            :show-file-list="false" accept="*/*">
            <el-button type="primary">选择文件</el-button>
        </el-upload>

        <div v-if="progress !== null" class="progress-container">
            <el-progress :percentage="progress" />
        </div>

        <div v-if="fileMd5">
            <div class="file-md5">
                <p><strong>文件 MD5:</strong> {{ fileMd5 }}</p>
            </div>
            <div class="file-md5">
                <p><strong>文件分片数量:</strong> {{ totalChunks }}</p>
            </div>
        </div>

        <div v-if="fileMd5" class="file-md5">
            <p><strong>上传状态:</strong> {{ uploadStatus }}</p>
        </div>
    </div>
</template>

<script>
import { reqChunkMerge, reqConvertVideo, reqUpload, reqUploadCheck } from "@/api/video";
import md5 from "js-md5";

export default {
    name: "Home",
    data() {
        return {
            file: null,
            chunkSize: 5 * 1024 * 1024, // 每个分片大小 5MB
            currentChunk: 0,
            totalChunks: 0,
            uploadStatus: "待上传",
            progress: null,
            fileMd5: null, // 保存计算出的 MD5 值
            concurrentUploads: 3 // 限制同时上传的分片数量
        };
    },
    methods: {
        // 选择文件后触发，计算文件 MD5
        handleFileChange(file) {
            this.currentChunk = 0;
            this.progress = null;
            this.fileMd5 = null; // 重置 MD5 值
            // 计算文件 MD5
            this.calculateFileMd5(file);
            // 返回 false 以阻止自动上传
            return false;
        },

        // 计算文件 MD5
        async calculateFileMd5(file) {
            this.uploadStatus = "计算MD5中..";
            const reader = new FileReader();

            reader.onload = async () => {
                const fileBuffer = reader.result;
                this.fileMd5 = md5(fileBuffer); // 计算 MD5
                await this.checkFileIsUpload(file) // 检查文件是否已经上传
            };

            reader.readAsArrayBuffer(file); // 以 ArrayBuffer 格式读取文件
        },

        // 检查文件是否已经上传
        async checkFileIsUpload(file) {
            this.uploadStatus = "检查文件是否已经上传";
            try {
                const res = await reqUploadCheck({
                    fileMd5: this.fileMd5
                });
                if (res && res.data) {
                    this.uploadStatus = "秒传，文件上传完成";
                    this.$message.success("秒传，上传成功");
                } else {
                    await this.startUpload(file);
                }
            } catch (error) {
                this.$message.error("检查上传状态失败");
                console.error(error);
            }
        },

        // 开始上传文件
        async startUpload(file) {
            this.totalChunks = Math.ceil(file.size / this.chunkSize);
            this.uploadStatus = "上传中...";
            // 异步上传多个分片
            await this.uploadChunks(file);
        },

        // 并行上传文件的多个分片
        async uploadChunks(file) {
            const promises = [];
            const chunkCount = this.totalChunks;

            for (let i = 0; i < chunkCount; i++) {
                // 控制并发数量
                if (promises.length >= this.concurrentUploads) {
                    // 等待某个分片上传完成
                    await Promise.race(promises);
                }

                // 上传当前分片
                const uploadPromise = this.uploadChunk(file, i).finally(() => {
                    // 每个分片上传完成后，移除该 promise
                    promises.splice(promises.findIndex(p => p === uploadPromise), 1);
                });

                promises.push(uploadPromise);
            }

            // 等待所有的分片都上传完成
            await Promise.all(promises);
            // 合并
            this.mergeChunk(file);
        },

        // 合并分片
        mergeChunk(file) {
            this.uploadStatus = "文件分片合并中";
            reqChunkMerge({
                filename: file.name,
                fileSize: file.size,
                totalChunk: this.totalChunks,
                fileMd5: this.fileMd5
            }).then(res => {
                this.uploadStatus = "文件合并完成";
                this.convertFile(res.data.id)
            }).catch(error => {
                this.$message.error("合并请求失败！");
            })
        },

        convertFile(id) {
            this.uploadStatus = "文件转码中";
            reqConvertVideo(id).then(res => {
                this.uploadStatus = "文件上传完成";
                this.$message.success("文件上传完成");
            }).catch(error => {
                this.$message.error("转码请求失败！");
            })
        },

        // 上传单个分片
        async uploadChunk(file, chunkIndex) {
            this.uploadStatus = `上传分片 ${chunkIndex + 1} 中...`;
            const start = chunkIndex * this.chunkSize;
            const end = Math.min((chunkIndex + 1) * this.chunkSize, file.size);
            const blob = file.slice(start, end);

            const formData = new FormData();
            formData.append("file", blob);
            formData.append("chunk", chunkIndex);
            formData.append("totalChunks", this.totalChunks);
            formData.append("fileMd5", this.fileMd5);

            try {
                await reqUpload(formData); // 异步上传当前分片
                this.currentChunk = chunkIndex + 1;
                this.progress = Math.round((this.currentChunk / this.totalChunks) * 100);
            } catch (error) {
                this.$message.error("上传请求失败！");
                this.uploadStatus = "上传失败";
                throw error;
            }
        },

        // 取消上传
        handleRemove() {
            this.currentChunk = 0;
            this.totalChunks = 0;
            this.progress = null;
            this.fileMd5 = null;
        }
    }
};
</script>

<style scoped>
.home {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    text-align: center;
}

.upload-demo {
    margin-bottom: 20px;
}

.progress-container {
    margin-top: 20px;
}

.file-md5 {
    margin-top: 20px;
    font-size: 14px;
    color: #666;
}
</style>
